package pl.edu.agh.administracja.networkconfig.model.util;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import pl.edu.agh.administracja.networkconfig.model.IpV4Address;
import pl.edu.agh.administracja.networkconfig.model.NetworkInterfaceConfig;
import pl.edu.agh.administracja.networkconfig.model.WirelessInterfaceConfig;

/**
 *
 * @author piotrek
 */
public class NetworkInterfacesResolver {
    
    private static final String ETH_PREFIX = "eth";
    private static final String WLAN_PREFIX = "wlan";
    
    private static final String MAIN_LINE_PATTERN = "((?:(?:wlan)|(?:eth))[0-9]+).+HWaddr[ ]+([0-9a-f:]{17})[ ]*";
    private static final String RECEIVED_SENT_LINE_PATTERN = "[ ]+RX bytes:[0-9]+[ ]+\\(([0-9.]+ [A-Z]+)\\)[ ]+TX bytes:[0-9]+[ ]+\\(([0-9.]+ [A-Z]+)\\)[ ]*";

    private static final String IP_ADDRESS_PATTERN = "[ ]+inet addr:([^ ]+).*Bcast:[^ ]+.*Mask:([0-9.]+)";
    private static final String DHCP_INFO_PATTERN = "\\s*iface \\w+ inet dhcp\\s*";
  
    private static final ParseAction[] parsers = {
        new ParseMainLine(),
        new ParseReceivedSent(),
        new ParseIpAndMaskAddress(),
        new getDefaultGateway()
    };

    private static List<NetworkInterfaceConfig> getInterfaces(String cmdResult) {
        List<NetworkInterfaceConfig> ret = new ArrayList<NetworkInterfaceConfig>();
        
        String[] lines = cmdResult.split("\n");
        int n = lines.length;
        
        NetworkInterfaceConfig config = null;
        boolean ignoreInterface = false;
        
        for(int i=0; i<n; i++){
            String line = lines[i];
            if(config == null || (!line.isEmpty() && line.charAt(0) != ' ')){
                if(line.startsWith(ETH_PREFIX)){
                    config = new NetworkInterfaceConfig();
                    ignoreInterface = false;
                }else{
                    if(line.startsWith(WLAN_PREFIX)){
                        config = new WirelessInterfaceConfig();
                        ignoreInterface = false;
                    }
                    else{
                        config = null;
                        ignoreInterface = true;
                    }
                }
                
                if(config != null)
                    ret.add(config);
            }
            
            if(!ignoreInterface)
                collectData(config, line);
        }
        return ret;
    }

    private NetworkInterfacesResolver(){
    }
    
    public static List<NetworkInterfaceConfig> getInterfaces(){
        String command = "ifconfig";
        String result = ShellExecutor.executeSynchronously(command);
        
        List<NetworkInterfaceConfig> activeInterfaces = getInterfaces(result);
        
        command = "ifconfig -a";
        result = ShellExecutor.executeSynchronously(command);
        List<NetworkInterfaceConfig> allInterfaces = getInterfaces(result);
        
        for(NetworkInterfaceConfig config : allInterfaces)
            if(activeInterfaces.contains(config))
                config.setOn(true);
        
        setDHCP(allInterfaces);
        
        return allInterfaces;
    }
    
    private static void setDHCP(List<NetworkInterfaceConfig> allInterfaces) {
        String command = "cat /etc/network/interfaces";
        String result = ShellExecutor.executeSynchronously(command);
        NetworkInterfaceConfig currentCfg = null;
        
        ParseAction [] localParsers = { new ParseDHCPInfo() };
        for(String line : result.split("\n")) {
            
            // pomijamy komentarze
            if(line.matches("\\s*#"))
                continue;
            
            // pusta linia oddziela kolejne interfejsy
            if("".equals(line.trim())) {
                currentCfg = null;
                continue;
            }
            
            // odpowiednia konfiguracja
            if(line.matches("auto \\w+")) {
                currentCfg = getCurrentCfg(allInterfaces, line.split(" ")[1]);
            }
            
            // jesli jest nullem to czekamy az sie ustawi odpowiedni
            if(currentCfg == null)
                continue;
            
            collectData(currentCfg, line, localParsers);
            System.out.println(line);
        }
        
    }
    
    private static NetworkInterfaceConfig getCurrentCfg(List<NetworkInterfaceConfig> allInterfaces, String name) {
        for(NetworkInterfaceConfig nic : allInterfaces) {
            if(nic.getName().equals(name))
                return nic;
        }
        return null;
    }
    
    private static void collectData(NetworkInterfaceConfig config, String line, ParseAction [] p){
        for(ParseAction parseAction : p){
            if(parseAction.parse(config, line))
                return;
        }
    }
            
    private static void collectData(NetworkInterfaceConfig config, String line){        
        collectData(config, line, parsers);
    }
    
    private static abstract class ParseAction{
        
        private String regex;
        private Pattern p;
        public abstract boolean parse(NetworkInterfaceConfig config, String line);
        
        public void setRegex(String regex){
            this.regex = regex;
             p = Pattern.compile(regex);
        }
        
        public String getRegex(){
            return regex;
        }
        
        public Matcher getMatcher(String str){
            return p.matcher(str);
        }
    }
    
    private static class ParseMainLine extends ParseAction{

        public ParseMainLine(){
            setRegex(MAIN_LINE_PATTERN);
        }
        
        @Override
        public boolean parse(NetworkInterfaceConfig config, String line) {
            Matcher matcher = getMatcher(line);
            if(!matcher.matches())
                return false;
            
            config.setName(matcher.group(1));
            config.setHardwareAddress(matcher.group(2));
            
            return true;
        }
        
    }
    
    private static class ParseReceivedSent extends ParseAction{

        public ParseReceivedSent(){
            setRegex(RECEIVED_SENT_LINE_PATTERN);
        }
        
        @Override
        public boolean parse(NetworkInterfaceConfig config, String line) {
            Matcher matcher = getMatcher(line);
            if(!matcher.matches())
                return false;
            
            config.setReceived(matcher.group(1));
            config.setSent(matcher.group(2));
            
            return true;
        }
        
    }
    
    private static class ParseIpAndMaskAddress extends ParseAction{

        public ParseIpAndMaskAddress(){
            setRegex(IP_ADDRESS_PATTERN);
        }
        
        @Override
        public boolean parse(NetworkInterfaceConfig config, String line) {
            Matcher matcher = getMatcher(line);
            if(!matcher.matches())
                return false;
            
            config.setIpAddress(IpV4Address.fromString(matcher.group(1)));
            config.setMask(IpV4Address.fromString(matcher.group(2)));
            
            return true;
        }
        
    }
    
    private static class ParseDHCPInfo extends ParseAction{

        public ParseDHCPInfo(){
            setRegex(DHCP_INFO_PATTERN);
        }
        
        @Override
        public boolean parse(NetworkInterfaceConfig config, String line) {
            Matcher matcher = getMatcher(line);
            if(!matcher.matches())
                return false;
            
            config.setIpViaDHCP(true);
            return true;
        }
        
    }
    
    private static class getDefaultGateway extends ParseAction{
        
        @Override
        public boolean parse(NetworkInterfaceConfig config, String line) {
            String result = ShellExecutor.executeSynchronously("route -n | awk '/^0.0.0.0/ {print $2}'");
            
            config.setDefaultGateway(IpV4Address.fromString(result.trim()));            
            return true;
        }
        
    }

}
